package com.heepay.common;

import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;


@Component
public class SignEncrypt {

    public static final String BC_PROV_ALGORITHM_SHA1RSA = "SHA1withRSA";

    private static final int MAX_ENCRYPT_BLOCK = 117;

    //private static final int MAX_DECRYPT_BLOCK = 128;
    private static final int MAX_DECRYPT_BLOCK = 256;

    public boolean verify(String data, String sign, PublicKey publicKey) {
        try {
            Signature signature = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA);
            signature.initVerify(publicKey);
            signature.update(data.getBytes(Constants.UTF));
            return signature.verify(org.bouncycastle.util.encoders.Base64.decode(sign.getBytes()));
        } catch (Exception e) {
            return false;
        }
    }


    public String sign(String obj, PrivateKey privateKey) {
        String sign = "";
        try {
            Signature signature = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA);
            signature.initSign(privateKey);
            signature.update(obj.getBytes(Constants.UTF));
            sign = new String(org.bouncycastle.util.encoders.Base64.encode(signature.sign()));
        } catch (Exception e) {
        }
        return sign;
    }

//    public String encrypt(String data, PublicKey publicKey) throws Exception {
//        Cipher cipher = Cipher.getInstance("RSA");
//        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//        int inputLen = data.getBytes().length;
//        ByteArrayOutputStream out = new ByteArrayOutputStream();
//        int offset = 0;
//        byte[] cache;
//        int i = 0;
//        while (inputLen - offset > 0) {
//            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
//                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
//            } else {
//                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
//            }
//            out.write(cache, 0, cache.length);
//            i++;
//            offset = i * MAX_ENCRYPT_BLOCK;
//        }
//        byte[] encryptedData = out.toByteArray();
//        out.close();
//        return new String(org.bouncycastle.util.encoders.Base64.encode(encryptedData), Constants.UTF);
//    }


    /**
     * 加密优化
     * 1.避免重复转换字符串到字节数组：
     * 在原始代码中，data.getBytes() 被多次调用，这会导致不必要的开销。应该将字节数组存储在一个变量中，然后重用这个变量。
     * 2.使用更高效的字节数组处理方式：
     * 使用 ByteArrayOutputStream 和 doFinal 方法处理数据块时，可以考虑直接使用一个固定大小的缓冲区来减少内存分配和复制的开销。
     * 3.正确计算和处理数据块：
     * 原始代码在处理最后一个数据块时可能存在问题，因为 offset 的计算方式可能导致数组越界或数据遗漏。
     * 4.使用更高效的Base64编码库：
     * 虽然 org.bouncycastle.util.encoders.Base64 是一个强大的库，但Java标准库中的 java.util.Base64 通常更高效且更易于集成。
     */

    public String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        // 将字符串转换为字节数组一次
        byte[] dataBytes = data.getBytes(Constants.UTF);
        int inputLen = dataBytes.length;

        // 使用ByteArrayOutputStream收集加密后的数据
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;

        while (inputLen - offset > 0) {
            int blockSize = Math.min(MAX_ENCRYPT_BLOCK, inputLen - offset);
            byte[] encryptedBlock = cipher.doFinal(dataBytes, offset, blockSize);
            out.write(encryptedBlock);
            offset += blockSize;
        }

        byte[] encryptedData = out.toByteArray();
        out.close();

        // 使用Java标准库的Base64编码
        return Base64.getEncoder().encodeToString(encryptedData);
    }



    public String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = org.bouncycastle.util.encoders.Base64.decode(data.getBytes());
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return new String(decryptedData, Constants.UTF);
    }

}
